package com.itheima.reggie.filter;

import com.alibaba.fastjson.JSON;
import com.itheima.reggie.entity.R;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebFilter(urlPatterns = "/*") //指定过滤(拦截)所有url
@Slf4j
public class LoginCheckFilter implements Filter {//注意:有很多Filter接口,要的是javax.servlet.Filter接口

    /**
     * 使用Filter过滤器需要在启动类前加@ServletComponentScan开启原始JavaEE组件扫描
     */

    //路径匹配器，支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    /*
    AntPathMatcher 拓展:

    介绍: Spring中提供的路径匹配器 ;

    通配符规则:

    | 符号 | 含义                   |
    | ---- | ---------------------- |
    | ?    | 匹配一个字符           |
    | *    | 匹配0个或多个字符      |
    | **   | 匹配0个或多个目录/字符 |
     */

    /**
     * 路径匹配，检查本次请求是否需要放行
     *
     * @param urls
     * @param requestURI
     * @return
     */
    public boolean check(String[] urls, String requestURI) {
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //获取url,用于判断该url是否是登录相关的路径,如果是就放行
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

        //获取本次请求的url
        String requestURI = httpServletRequest.getRequestURI(); //注意URI是从项目名称往后的站内资源路径,没有前面的localhost:8080只有/backend/page/login/login.html
        log.info("当前过滤器拦截到的路径:{}", requestURI);

        //定义直接放行的请求路径
        String[] urls = new String[]{

                "/employee/login",//后台登录请求url
                "/employee/logout",//后台注销请求url
                "/backend/**",//全部商家端页面(后台)

                "/common/**",//文件上传下载,便于测试

                "/front/**",//全部用户端页面(前台)
                "/user/getVerificationCode",//前台发验证码请求url
                "/user/login",

                //for Knife4j
                "/doc.html",
                "/webjars/**",
                "/swagger-resources",
                "/v2/api-docs"

        };//前台登录请求url
        /*注意:此处实际上放行了所有静态资源页面,以及登录注销的请求路径,除此之外其他的请求controller路径一概会被拦截*/

        //判断本次请求是否需要放行
        if (check(urls, requestURI)) {
            //直接放行
            filterChain.doFilter(servletRequest, servletResponse);
            return;//不写return,放行请求后,等到返回响应时,又会回来继续执行后面的代码
        }

        //能执行到这里说明,该url需要登录才能访问
        HttpSession session = httpServletRequest.getSession();
        if (/*session.getAttribute("isLogin") != null ||*/ session.getAttribute("user") != null) {/**此处有漏洞,无法区分是后台登录还是前台登录,导致同一次会话中只要前台或者后台已登录,另一个就无需登录直接被放行了*/
            //表示已登录,放行
            filterChain.doFilter(servletRequest, servletResponse);
            return;//不写return,放行请求后,等到返回响应时,又会回来继续执行后面的代码
        }/* else {
            //表示还未登录,返回登录页
            log.info("用户未登录");
            R notlogin = R.error("NOTLOGIN");//前端(前台后台相同)已经写死code=0 msg=NOTLOGIN表示未登录,将会跳转到登录页
            String JSONString = JSON.toJSONString(notlogin);
            httpServletResponse.setContentType("application/json");//告诉前端响应内容是JSON字符串(可加可不加)
            httpServletResponse.getWriter().write(JSONString);
        }*/


        /**使用JWT令牌*/
        /*
          1、获取请求请求头中的token  Authorization  request.getHeader()
          2、判断不为空，接着解析，不报错，就能获取到载荷信息 ： 员工id
        */

        //从请求头中获取tokenString
        String tokenString = ((HttpServletRequest) servletRequest).getHeader("Authorization");
        System.err.println("拿到token:" + tokenString);
        //拿到token:eyJhbGciOiJIUzI1NiJ9.eyJDdXJyZW50TG9naW5Vc2VySWQiOjEsImV4cCI6MTY4MzYzNjc5NH0.8xjJLTEW99M9yCVgvkeBKHQHHJmNCqnFpABNE3xy7pw

        try {
            if (tokenString != null) {
                //表示已登录,校验令牌
                JwtParser parser = Jwts.parser();//获得JWT令牌解析器
                parser.setSigningKey("itcast");
                Jws<Claims> claimsJws = parser.parseClaimsJws(tokenString);
                Claims body = claimsJws.getBody();

                System.err.println(claimsJws.getSignature());
                //8xjJLTEW99M9yCVgvkeBKHQHHJmNCqnFpABNE3xy7pw
                System.err.println(body);
                //{CurrentLoginUserId=1, exp=1683636794}

                //放行
                filterChain.doFilter(servletRequest, servletResponse);
                return;//不写return,放行请求后,等到返回响应时,又会回来继续执行后面的代码
            }
            throw new Exception();
        } catch (Exception e) {
            //表示还未登录,返回登录页
            log.info("用户未登录");
            R notlogin = R.error("NOTLOGIN");//前端(前台后台相同)已经写死code=0 msg=NOTLOGIN表示未登录,将会跳转到登录页
            String JSONString = JSON.toJSONString(notlogin);
            httpServletResponse.setContentType("application/json");//告诉前端响应内容是JSON字符串(可加可不加)
            httpServletResponse.getWriter().write(JSONString);
        }
    }
}
